using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Windows;
//using ViewOn.Kernel;
//using ViewOn.Kernel.Media;
//using ViewOn.Kernel.Tasks;
//using ViewOn.Kernel.MediaDataCopy;
//using iPodDeviceAddIn;
//using ViewOn.AddIns.MyLibrary;
//using ViewOn.AddIns.MyLibrary.Import;
//using ViewOn.Properties;
using Un4seen.Bass;
using Un4seen.Bass.AddOn.Tags;
using Un4seen.Bass.AddOn.Cd;
using Un4seen.Bass.AddOn.Wma;
using ViewOn.Properties;
using ViewOnCore.Helper.Debug;
using ViewOnCore.HostContract;
using ViewOnCore.HostContract.Media;
using ViewOnCore.HostContract.Tasks;
using ViewOnCore.Device;

namespace ViewOn.AddInsHostContract.Media
{
    /// <summary>
    /// This task rip a musical CD.
    /// </summary>
    internal sealed class RipMusicCDTask : Task
    {

        #region Variables

        private List<MediaData> _sourceMusics;
        private MediaFeature _feature;
        private MediaItem _featureItem;
        private string _path;

        #endregion

        #region Constructor

        public RipMusicCDTask(MediaFeature feature, MediaItem featureItem, List<MediaData> sourceMusics)
        {
            _feature = feature;
            _featureItem = featureItem;
            _sourceMusics = sourceMusics;
        }

        public RipMusicCDTask(string path, List<MediaData> sourceMusics)
        {
            _path = path;
            _sourceMusics = sourceMusics;
        }

        #endregion

        #region Properties

        public override string Message
        {
            get
            {
                return Globalization.MediaCopy_Ripping;
            }
        }

        #endregion

        #region Execute

        public override void Execute()
        {
            try
            {
                Export();
            }
            catch (Exception exception)
            {
                LogManager.Handle(exception);
            }
        }

        #endregion

        #region Export

        private void Export()
        {
            int driveId = (int)Host.MediaService.CurrentMediaDeviceFeature.GetProperty("DriveId");

            // Lock Drive
            BassCd.BASS_CD_Door(driveId, BASSCDDoor.BASS_CD_DOOR_LOCK);

            int[] bitRates = BassWma.BASS_WMA_EncodeGetRates(44100, 2, BASSWMAEncode.BASS_WMA_ENCODE_RATES_CBR);

            foreach (Music music in _sourceMusics)
            {
                //----- Create the file name
                string outputFileName = GetOutputFileName(music);

                int streamHandle = BassCd.BASS_CD_StreamCreateFile(music.Path, BASSStream.BASS_STREAM_DECODE);
                if (streamHandle == 0)
                {
                    int error = Un4seen.Bass.Bass.BASS_ErrorGetCode();
                    //Un4seen.Bass.BASSErrorCode BASS_ERROR_DEVICE
                    LogManager.Trace(LogType.Warning, "Unable to rip DVD/CD track");
                    continue;
                }

                // Open Output File
                // We Know the Freq will be 44100.
                int encoderHandle = BassWma.BASS_WMA_EncodeOpenFile(44100, 2, BASSWMAEncode.BASS_WMA_ENCODE_DEFAULT, bitRates[0], outputFileName);
                if (encoderHandle == 0)
                {
                    LogManager.Trace(LogType.Warning, "Unable to rip DVD/CD track");
                    continue;
                }

                //---- Copy our tags from the source to the new destination file...
                //TAG_INFO tagInfo = BassTags.BASS_TAG_GetFromFile(music.Path);
                //if (tagInfo != null)
                //{
                //    bool ok = true;
                //    if (music.Album.Length > 0)
                //        ok &= BassWma.BASS_WMA_EncodeSetTag(encoderHandle, "WM/AlbumTitle", music.Album);
                //    if (music.Artist.Length > 0)
                //        ok &= BassWma.BASS_WMA_EncodeSetTag(encoderHandle, "Author", music.Artist);
                //    if (music.Comment.Length > 0)
                //        ok &= BassWma.BASS_WMA_EncodeSetTag(encoderHandle, "Description", music.Comment);
                //    if (music.Composer.Length > 0)
                //        ok &= BassWma.BASS_WMA_EncodeSetTag(encoderHandle, "WM/Composer", music.Composer);
                //    ok &= BassWma.BASS_WMA_EncodeSetTag(encoderHandle, "WM/EncodedBy", "www.viewon.tv");
                //    if (music.Genre.Length > 0)
                //        ok &= BassWma.BASS_WMA_EncodeSetTag(encoderHandle, "WM/Genre", music.Genre);
                //    if (music.Title.Length > 0)
                //        ok &= BassWma.BASS_WMA_EncodeSetTag(encoderHandle, "Title", music.Title);
                //    ok &= BassWma.BASS_WMA_EncodeSetTag(encoderHandle, "WM/TrackNumber", music.Track.ToString());
                //    ok &= BassWma.BASS_WMA_EncodeSetTag(encoderHandle, "WM/Year", music.Year.ToString());
                //}

                //---- Encode the data
                //byte[] encBuffer = new byte[65536]; // our encoder buffer (32KB x 16-bit)
                //while (Bass.BASS_ChannelIsActive(streamHandle) == (int)BASSActive.BASS_ACTIVE_PLAYING)
                //{
                //    // get the decoded sample data
                //    int len = Bass.BASS_ChannelGetData(streamHandle, ref encBuffer[0], encBuffer.Length);

                //    // write the data to the encoder
                //    bool encodeOk = BassWma.BASS_WMA_EncodeWrite(encoderHandle, ref encBuffer[0], len);
                //    if (!encodeOk)
                //    {
                //        int error = Un4seen.Bass.Bass.BASS_ErrorGetCode();
                //        //Un4seen.Bass.BASSErrorCode BASS_ERROR_DEVICE
                //        LogManager.Trace(LogType.Warning, "Unable to rip DVD/CD track");
                //        break;
                //    }

                //    //long total = Bass.BASS_ChannelGetLength(stream);
                //    //long pos = Bass.BASS_ChannelGetPosition(stream);
                //}

                // write the data to the encoder
                //byte[] encBuffer = new byte[65536];
                //int len = Bass.BASS_ChannelGetData(streamHandle, ref encBuffer[0], 65536);
                //while (BassWma.BASS_WMA_EncodeWrite(encoderHandle, ref encBuffer[0], len))
                //{
                //    len = Bass.BASS_ChannelGetData(streamHandle, ref encBuffer[0], 65536);
                //}

                byte[] encBuffer = new byte[65536];
                unsafe
                {
                    // pointers to managed objects need to be fixed
                    fixed (byte* p = encBuffer)
                    {
                        while (Bass.BASS_ChannelIsActive(streamHandle) == (int)BASSActive.BASS_ACTIVE_PLAYING)
                        {
                            int len = Bass.BASS_ChannelGetData(streamHandle, (IntPtr)p, 65536);
                            if (len > 0)
                                BassWma.BASS_WMA_EncodeWrite(encoderHandle, (IntPtr)p, len);
                        }
                    }
                }

                //---- Clean
                BassWma.BASS_WMA_EncodeClose(encoderHandle);
                Bass.BASS_StreamFree(streamHandle);
            }

            BassCd.BASS_CD_Door(driveId, BASSCDDoor.BASS_CD_DOOR_UNLOCK);

            //---- When it is a simple "Disk copy", all is done -> exit
            if (_path != null)
                return;

            //---- Search for the "Music feature" of MyLibrary
            MediaFeature musicFeature = Host.MediaService.FindDeviceFeatureByHandle("MyLibraryMediaDeviceMusicFeature");

            //---- Add to the device
            foreach (Music music in _sourceMusics)
            {
                string outputFileName = GetOutputFileName(music);

                Music addedMusic = new Music(music);
                addedMusic.Path = outputFileName;

                //-- Import to MyLibrary
                musicFeature.AddMediaData(null, addedMusic, false);

                //-- Update the tag of the local file
                //_feature.UpdateTag(addedMusic);

                if (_featureItem != null)
                {
                    Music playlistMusic = new Music(music);
                    playlistMusic.Path = outputFileName;

                    _featureItem.AddMediaData(playlistMusic);
                }
            }

            if (_featureItem != null)
                _featureItem.OnSaveChanges();

            musicFeature.OnSaveChanges();
        }

        #endregion

        #region Helpers

        /// <summary>
        /// Create a "name" for the resulting file on disk based on the information
        /// contained in the media.
        /// </summary>
        /// <param name="music">The music/song to rip</param>
        /// <returns>Returns the name</returns>
        private string GetOutputFileName(Music music)
        {
            string copyFormat = "[Artist] - [Album] - [Title]";
            string copyFolder = Host.SettingsService.CachePath;
            if (_path != null)
                copyFolder = _path;

            string str = (copyFormat + ".wma").Replace("[Title]", this.GetSafeTag(music.Title)).Replace("[Artist]", this.GetSafeTag(music.Artist)).Replace("[Album]", this.GetSafeTag(music.Album)).Replace("[TrackNumber]", music.Track.ToString("00")).Replace("[Genre]", this.GetSafeTag(music.Genre)).Replace("[Composer]", this.GetSafeTag(music.Composer));
            while (str.Contains(@" \"))
                str = str.Replace(@" \", @"\");

            if (!copyFolder.EndsWith(@"\"))
                copyFolder += @"\";

            copyFolder = copyFolder.Replace("\\", "/");

            return copyFolder + str;
        }

        private string GetSafeTag(string tag)
        {
            foreach (char ch in Path.GetInvalidFileNameChars())
                tag = tag.Replace(ch, '_');
            return tag.Trim();
        }

        #endregion

    }

}
